Java基础知识(十三)
接口
接口的基本定义
1、接口
是只由抽象方法和全局变量组成的类,使用interface
关键字定义。
interface A { // 定义接口
public static final String MSG = "Hello"; // 全局常量
public abstract void print(); // 抽象方法
}
由于接口中存在抽象方法,因此接口对象不能直接实例化,其使用原则如下:
(1)接口必须要有子类,子类可以使用implements关键字实现多接口;
(2)接口的子类(非抽象类时),必须对接口的抽象方法进行覆写;
(3)接口的对象可以利用子类对象通过向上转型实现实例化。
interface A { // 定义接口
public static final String MSG = "Hello"; // 全局常量
public abstract void print(); // 抽象方法
}
interface B {
public abstract void get();
}
class X implements A, B { // X 实现A和B两个接口
public void print() {
System.out.println("A.抽象方法");
}
public void get() {
System.out.println("B.抽象方法");
}
}
public class Demo {
public static void main(String[] args) {
X x = new X(); // 实例化子类对象
A a = x; // 向上转型
B b = x; // 向上转型
a.print();
b.get();
}
}
X是A和B的子类,因此X的对象可以变为A或者B接口的对象。
public class Demo {
public static void main(String[] args) {
A a = new X();
B b = (B) a;
b.get(); // B.抽象方法
System.out.println(a instanceof A); // true
System.out.println(b instanceof B); // true
}
}
从结构上来说,A和B接口没有直接关系,但是两个接口拥有共同的子类X,最终实例化的是X,这个子类向上转型为B类对象,因此代码可以执行。
2、子类同时继承类和接口时,语法:class X extends A implements B {}
接口的组成只有抽象方法和全局变量,因此可以不写abstract
和public static final
,并且方法是否使用public
定义都是一样的,因为接口只能使用public
权限。
|interface A{
public static final String MSG = "Hello"; //全局常量
public abstract void print(); //抽象方法
}
|interface A{
String MSG = "Hello"; //全局常量
void print(); //抽象方法
}
上述代码是等价的。在接口中,默认访问权限为public
,而不是default
。但开发时,定义接口中的方法时要写上public。
3、一个抽象类只能继承一个抽象类,但一个接口可以使用extends
关键字同时继承多个接口,接口不能继承抽象类。
interface A {
public void funA();
}
interface B {
public void funB();
}
interface C extends A, B {
public void funC();
}
class X implements C {
public void funA() {
}
public void funB() {
}
public void funC() {
}
}
从继承上来说,抽象类的限制比接口多:
(1)一个抽象类只能继承一个抽象父类,而接口没有该限制;
(2)一个子类只能继承一个抽象类,但可以实现多接口。
因此Java中接口的功能是解决单继承限制。
4、从概念而言,接口只能由抽象方法和全局变量
组成。但接口中可以定义普通内部类、抽象内部类、内部接口
。这些内部接口不受接口的概念限制。
interface A {
public void funA();
abstract class B { // 定义抽象内部类
public abstract void funB();
}
}
class X implements A { // X实现A接口
public void funA() {
}
class Y extends B { // 抽象内部类的子类
public void funB() {
}
}
}
上述代码形式,几乎不会用到。
5、在接口中使用static
定义一个内部接口,相当于外部接口。
interface A {
public void funA();
static interface B { // 相当于外部接口
public void funB();
}
}
class X implements A.B { // X实现B接口
public void funB() {
}
}
总结:
接口在实际开发中的三大作用:
(1)定义不同层之间的操作标准;
(2)表示一种操作的能力;
(3)表示将服务器端的远程方法视图暴露给客户端。
定义标准
根据上图编写代码:电脑利用USB接口标准和其他设备关联。
范例:定义USB标准
// 标准可以连接不同层的操作类
interface USB { // 接口就是标准
public void start();
public void stop();
}
范例:定义电脑
class Computer{
public void plugin(USB usb){ // 插入USB设备
usb.start();
usb.stop();
}
}
不论是什么设备,只要实现了USB标准,就可以在电脑上使用。
范例:定义U盘
class Flash implements USB {
public void start() {
System.out.println("U盘开始使用");
}
public void stop() {
System.out.println("U盘停止使用");
}
}
范例:定义打印机
class Print implements USB {
public void start() {
System.out.println("打印机开始使用");
}
public void stop() {
System.out.println("打印机停止使用");
}
}
范例:测试
public class Demo {
public static void main(String[] args) {
Computer com = new Computer();
com.plugin(new Flash());
com.plugin(new Print());
}
}
Java中,标准就是被定义为接口。
工厂设计模式
interface Fruit {
public void eat();
}
class Apple implements Fruit {
public void eat() {
System.out.println("吃苹果");
}
}
上述代码通过主方法可以取得Fruit对象,但存在如下问题:
附:要确定代码是否合理,标准如下:
(1)客户端调用简单,不用关注具体实现;
(2)客户端之外的代码修改,不影响用户的使用,即用户不用关注代码的变更。
由上述标准可知该程序的问题是一个接口不一定只有子类,现在添加一个Fruit子类:
class Orange implements Fruit {
public void eat() {
System.out.println("吃橘子");
}
}
客户端要使用这个新子类,需要修改代码:
public class Demo {
public static void main(String[] args) {
Fruit f = new Orange ();
f.eat(); // 吃橘子
}
}
在上述代码,我们最关注的是如何获得Fruit对象,之后进行方法调用。至于该对象是怎么实例的,不是客户端的工作。
1、上述程序中,客户端每次更换对象,就需要修改主方法的代码,这不符合标准。该问题的关键在于new
的使用,这种问题是因为耦合度太高(两者联系程度太高)
。耦合度太高,代码不便于维护。
解决思路:参考Java虚拟机的设定-程序→JVM→适应不同的操作系统。
范例:增加一个过渡
class Factory { // 工厂设计模式
public static Fruit getInstance(String className) {
if ("apple".equals(className)) {
return new Apple();
} else if ("orange".equals(className)) {
return new Orange();
} else {
return null;
}
}
}
public class Demo {
public static void main(String[] args) {
Fruit f = Factory.getInstance("apple");
f.eat(); // 吃苹果
}
}
此时,所有子类对于客户端是不可见的,因为Fruit对象的实例化是通过Factory对象获取的,日后扩充Fruit子类时,只需修改Factory即可,不用修改客户端代码。
上图描述的是工厂设计模式,客户端可见的只有接口和Factory。
代理设计模式
代理设计模式的核心在于有一个主题操作接口(可能有很多接口),核心操作类只完成核心功能,而代理主题负责完成所有与核心操作有关的辅助性操作。
1、以去餐馆吃饭为例
,外人只看到吃饭,看不到厨师和顾客。皇帝完成核心业务,厨师完成辅助业务。
package com.java.demo;
interface Subject { // 核心业务
public void eat();
}
class RealSubject implements Subject { // 实际进行核心业务的对象
public void eat() {
System.out.println("顾客在吃饭");
}
}
class ProxySubject implements Subject { // 执行代理操作的对象
private Subject subject; // 接收进行核心业务的对象
public ProxySubject(Subject subject) {
this.subject = subject;
}
public void prepared() {
System.out.println("为吃做准备");
}
public void eat() {
this.prepared();
this.subject.eat();
this.destory();
}
public void destory() {
System.out.println("为吃收尾");
}
}
public class Demo {
public static void main(String[] args) {
Subject sub = new ProxySubject(new RealSubject());
sub.eat(); // 调用代理操作
}
}
接口与抽象类的区别
No. | 区别 | 抽象类 | 接口 |
---|---|---|---|
1 | 关键字 | abstract class | interface |
2 | 组成 | 构造方法、普通方法、抽象方法、static方法、变量、常量 | 抽象方法,全局常量 |
3 | 子类使用 | class 子类 extends 抽象类 | class 子类implements 接口,接口… |
4 | 关系 | 抽象类可以实现多接口 | 接口不能继承抽象类,却可以继承多个接口 |
5 | 权限 | 可以使用任意权限 | 只能使用public权限 |
6 | 限制 | 单继承局限 | 没有单继承局限 |
7 | 子类 | 抽象类和接口都必须有子类,子类必须要覆写全部抽象方法 | 抽象类和接口都必须有子类,子类必须要覆写全部抽象方法 |
8 | 实例化对象 | 依靠子类对象的向上转型进行对象的实例化 | 依靠子类对象的向上转型进行对象的实例化 |
经过比较发现,抽象类存在单继承限制,因此当抽象类和接口都可以使用时,优先考虑接口。
(1)当进行公共操作时,要定义接口;
(2)有了接口就需要使用子类完善方法
(3)自定义的接口,不能直接实例化接口子类,应使用工厂设计模式。
This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://yov.oschina.io/article/Java/Java Base/Java基础知识(十三)/